/*
    <one line to give the library's name and an idea of what it does.>
    Copyright (C) 2013  Guillaume <email>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/


#ifndef MSGENERALIZEDCOORDINATE_H
#define MSGENERALIZEDCOORDINATE_H

#include <msPhysicalInterface.h>
#include <msScalarVariable.h>

namespace atomism
{
       
class msScalarFunction;
      
class msGeneralizedCoordinates : public msPhysicalInterface
{
 friend class msScalarFunction;
 
      //!\name from msRegister 
      //@{
      static msRegistrar Registrar;
      static bool  isGeneralizedCoordinatesRegisteredInPython;
      static msRegister* createInstance() { return new msGeneralizedCoordinates; }
      //@}
      
protected:
      
      void registryInPython(); 
      
public:
    
    template<class Value,class Value2>
    class UnfreezedIterator : public boost::iterator_facade<UnfreezedIterator<Value,Value2>, 
                                                          Value,
							  boost::forward_traversal_tag>
       {
	public:
	   
	   UnfreezedIterator() : Variable(0), GeneralizedCoordinates(0), Indice(-1) {};
	   UnfreezedIterator(Value2* s) : Variable(0), GeneralizedCoordinates(s), Indice(-1) 
	         {if(s!=0)increment();
		  };
	   explicit UnfreezedIterator(Value* v) : Variable(v) {};
	   Value& dereference() const {return *Variable;}
	   
	   msChildren<msScalarVariable>::iterator toIterator(){return GeneralizedCoordinates->begin()+Indice;};
	   msChildren<msScalarVariable>::const_iterator toIterator()const{return GeneralizedCoordinates->begin()+Indice;};
	   
	 private:
	   
	 friend class boost::iterator_core_access;
	   
	 void increment() { 
                 Indice++;
 		 if( Indice==int(GeneralizedCoordinates->noOfDim()) ){ Variable=0; return; } 
                    else Variable=GeneralizedCoordinates->operator[](Indice).get();
		 while( (Variable->dq==0) && ( Indice < int(GeneralizedCoordinates->noOfDim()) - 1 ) ){
		       Indice++;Variable=GeneralizedCoordinates->operator[](Indice).get(); 
                       }
		 if( Variable->dq !=0 ) return;
                 Variable=0; return; 
		 }
	   bool equal(UnfreezedIterator<Value,Value2> const& other) const{
                return this->Variable == other.Variable; 
	        }
 	   
	 protected:
	     
           Value* Variable;
	   Value2* GeneralizedCoordinates;
	   int Indice;
	   };
	  
    typedef UnfreezedIterator<msScalarVariable,msGeneralizedCoordinates> UnfreezIt;
    typedef UnfreezedIterator<const msScalarVariable,const msGeneralizedCoordinates> const_UnfreezIt;
    typedef msChildren<msScalarVariable>::iterator iterator;
    typedef msChildren<msScalarVariable>::const_iterator const_iterator;
    
 protected:
    
     void initialize()  { 
             msPhysicalInterface::initialize();
             declareChildren<msScalarVariable>(Variables,"Variables");
             };
	     
     void update(){ msPhysicalInterface::update();}
     
     msGeneralizedCoordinates() : msPhysicalInterface(){
	     constructVar("msGeneralizedCoordinates","GeneralizedCoordinates","generalizedCoordinates");
             }    
 public:
   
     static boost::shared_ptr<msGeneralizedCoordinates> New(boost::shared_ptr<msUnitsManager> units){
             
               boost::shared_ptr<msGeneralizedCoordinates> T( new msGeneralizedCoordinates );
	       LOGGER_ENTER_FUNCTION_DBG("msGeneralizedCoordinates::New(boost::shared_ptr<msUnitsManager> units)",T->getFullId());
	       T->initialize(); T->setUnits(units); 
	       LOGGER_EXIT_FUNCTION();
	       return T;
	       }
     virtual boost::shared_ptr<msTreeMapper> clone() { 
            boost::shared_ptr<msGeneralizedCoordinates> clone = New( msUnitsManager::New() ); 
	    clone->msTreeMapper::operator=(*this);					      
	    return clone;
            }	     
    boost::shared_ptr<msTreeMapper> addVar(boost::shared_ptr<msScalarVariable> var);


    size_t noOfDim() const;
    
    size_t noOfActive() const;
       
    double getVolumeDq() const;
    
    double getVolumeDq(const msUnitsManager& units) const;
    
    double getdq(const_iterator q, const msUnitsManager& units) const;

    double value(size_t i, double v) const { return getVariable(i)->value(v); };

    double getValue(const_iterator q, const msUnitsManager& units) const;

    double getMin(const_iterator q, const msUnitsManager& units) const;

    double getMax(const_iterator q, const msUnitsManager& units) const;

    int getIntegrationDim() const;

    void getUnfreezedValues(const msUnitsManager& units, vector_type& vec) const;
         
    boost::shared_ptr<msScalarVariable> operator[](std::string str) const {return getVariable(str);};
   
    boost::shared_ptr<msScalarVariable> operator[]      (int i)       {return getVariable(i);};

    const boost::shared_ptr<msScalarVariable> operator[](int i) const {return getVariable(i);};
 


    void clear() { if( Variables.size() != 0 )	
	              msTreeMapper::clearChildren<msScalarVariable>(Variables); 
		   }

    boost::shared_ptr<msTreeMapper> setValue(iterator qi, double v, const msUnitsManager& units);

    boost::shared_ptr<msTreeMapper> setMinValues();
	
    boost::shared_ptr<msTreeMapper> setUnfreezedValues(const msUnitsManager& units, const vector_type& vec);

    void setRandom();
    
    boost::shared_ptr<msTreeMapper> setCoordinate(size_t i,double val);

    boost::shared_ptr<msTreeMapper> setCoordinates(const vector<double>& val);
    
    
    std::vector<boost::shared_ptr<msScalarVariable> > getVariables() const;
    
    iterator begin(){return Variables.begin();}
    const_iterator begin()const {return Variables.begin();}
    
    UnfreezIt beginUnfreezed() { UnfreezIt it(this); return it; }
    const_UnfreezIt beginUnfreezed() const { const_UnfreezIt it(this); return it; }
      
    iterator end(){return Variables.end();}
    const_iterator end()const {return Variables.end();}
    
    UnfreezIt endUnfreezed()             { return UnfreezIt(); };
    const_UnfreezIt endUnfreezed() const { return const_UnfreezIt(); };
    
    

    std::ostream& writePosAndField(std::ostream& out,double value);
    std::ostream& writePos(std::ostream& out);
    
    const msScalarVariable& getVariable_const(int i) const {return *(getVariable(i));};
    
    std::ostream& print(std::ostream& out)    const;
    std::ostream& abstract(std::ostream& out) const;

    boost::shared_ptr<msScalarVariable> getVariable(std::string str) const;
    
    boost::shared_ptr<msScalarVariable> getVariable(size_t i) const;
	
    protected:
  
    msChildren<msScalarVariable> Variables;
    
  };
std::ostream& operator<<(std::ostream& out,const msGeneralizedCoordinates& gencoor);
std::ostream& operator<<(std::ostream& out,const msScalarVariable& gencoor);
}
#endif // MSGENERALIZEDCOORDINATE_H
